﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

namespace ProjetSMA.Model
{
    public class ItemMove
    {
        protected Element m_position_now;
        protected Element m_position_next;
        protected string m_name_direction = "_none";
        protected List<Element> m_elements_visible = new List<Element>();
        protected List<Element> m_elements_visible_tmp = new List<Element>();
        protected List<Element> m_elements_visible_tmp_bis = new List<Element>();

        protected List<Element> m_elements_viewed = new List<Element>();
        

        protected List<Element> m_element_visited = new List<Element>();
        protected List<Element> m_path = new List<Element>();

        protected int m_range = 4;
        protected int m_range_perception = 2;

        protected List<ItemMove> m_item_viewed = new List<ItemMove>();
        protected List<ItemMove> m_item_friends;
        protected List<ItemMove> m_item_foes;

        protected List<Element> m_perception = new List<Element>();
        protected List<Element> m_perception_tmp = new List<Element>();


        protected string m_name = "";

        protected Decision m_decision = new Decision();
        protected List<ItemMove> m_children = new List<ItemMove>();

        protected bool m_is_killed = false;
        protected bool m_want_breed = false;

        protected float m_growth_max = 25;
        protected float m_growth = 25;
        protected string m_type_decision = "_";
        protected int m_cpt_child = 0;
        protected int m_cpt_child_max = 2;
        protected int m_time = 4000;

        protected int m_starvation = 0;
        protected int m_starvation_max = 35;


        protected System.Timers.Timer m_timer;

        public ItemMove()
        {

        }

        public ItemMove(Element par_position_now, float par_growth, string par_name)
        {
            m_position_now = par_position_now;
            m_growth = par_growth;
            m_name = par_name;

            m_timer = new System.Timers.Timer(1000);
            m_timer.Elapsed += new ElapsedEventHandler(Tick_Growing);
            m_timer.Interval = (m_time) * (1);
            m_timer.Enabled = true;
            m_timer.Start();
        }

        public void Tick_Growing(object sender, EventArgs e)
        {
            if (m_growth < m_growth_max)
                m_growth += 1f;

            if (m_starvation < m_starvation_max)
            {
                m_starvation += 1;
            }
            else
            {
                // if no prey eaten the predator die
                Killed = true;
                try
                {
                    m_position_now.Occupants.Remove(this);
                }
                catch (Exception edd)
                {

                }
            }
        }

        public void MoveRandom()
        {
            Random loc_random = new Random();
            int par_test = loc_random.Next(0, 8);

            switch (par_test)
            {
                case 0:  TestMoveOpposite( m_position_now.North    ); m_name_direction = "_north";      break;
                case 1:  TestMoveOpposite( m_position_now.NorthEast); m_name_direction = "_north_east"; break;
                case 2:  TestMoveOpposite( m_position_now.East);      m_name_direction = "_east";       break;
                case 3:  TestMoveOpposite( m_position_now.SouthEast); m_name_direction = "_south_east"; break;

                case 4:  TestMoveOpposite( m_position_now.South);     m_name_direction = "_south";      break;
                case 5:  TestMoveOpposite( m_position_now.SouthWest); m_name_direction = "_south_west"; break;
                case 6:  TestMoveOpposite( m_position_now.West);      m_name_direction = "_west";       break;
                case 7:  TestMoveOpposite( m_position_now.NorthWest); m_name_direction = "_north_west"; break;
            }

            if (!TestRock(m_position_next) && m_position_next != null)
            {
                m_position_now.Occupants.Remove(this);
                m_position_now = m_position_next;
                m_position_next.Occupants.Add(this);
            }
        }

        public void TestMoveOpposite(Element par_dest)
        {
            if (m_position_now == par_dest)
            {
                if( m_position_now == m_position_now.North)
                    m_position_next = m_position_now.South;
                else if (m_position_now == m_position_now.NorthEast)
                    m_position_next = m_position_now.SouthWest;
                else if (m_position_now == m_position_now.East)
                    m_position_next = m_position_now.West;
                else if (m_position_now == m_position_now.SouthEast)
                    m_position_next = m_position_now.NorthWest;
                else if (m_position_now == m_position_now.South)
                    m_position_next = m_position_now.North;
                else if (m_position_now == m_position_now.SouthWest)
                    m_position_next = m_position_now.NorthEast;
                else if (m_position_now == m_position_now.West)
                    m_position_next = m_position_now.East;
                else if (m_position_now == m_position_now.NorthWest)
                    m_position_next = m_position_now.NorthEast;
            }
            else
            {
                m_position_next = par_dest;
            }
        }

        // general behaviour
        public virtual void Live()
        {
            ResetAllLists();
            DeterminedVisiblesElements();
            //DeterminedViewedItemsMove();
            DeterminedPerception();
            Detection();          
        }

        public void Detection()
        {
            m_item_viewed = new List<ItemMove>();
            foreach (Element loc_elem in m_elements_visible)
            {               
                foreach (ItemMove loc_item in loc_elem.Occupants)
                {                  
                    m_item_viewed.Add(loc_item);
                }
            }
            foreach (Element loc_elem in m_perception)
            {              
                foreach (ItemMove loc_item in loc_elem.Occupants)
                {                  
                   m_item_viewed.Add(loc_item);
                }
            }
        }

       

        public void SetDirection(Element par_elem)
        {
            if (par_elem == m_position_now.North)
                m_name_direction = "_north";
            else if (par_elem == m_position_now.NorthEast)
                m_name_direction = "_north_east";
            else if (par_elem == m_position_now.East)
                m_name_direction = "_east";
            else if (par_elem == m_position_now.SouthEast)
                m_name_direction = "_south_east";
            else if (par_elem == m_position_now.South)
                m_name_direction = "_south";
            else if (par_elem == m_position_now.SouthWest)
                m_name_direction = "_south_west";
            else if (par_elem == m_position_now.West)
                m_name_direction = "_west";
            else if (par_elem == m_position_now.NorthWest)
                m_name_direction = "_north_west";    
            
             

        }

        private void ResetAllLists()
        {
            m_item_viewed = new List<ItemMove>();
            m_item_friends = new List<ItemMove>();
            m_item_foes = new List<ItemMove>();
            m_path = new List<Element>();
        }

        public void DeterminedVisiblesElements()
        {
            m_elements_viewed = m_elements_visible;
            m_elements_visible = new List<Element>();
            m_elements_visible_tmp = new List<Element>();
            m_element_visited = new List<Element>();
            m_elements_visible_tmp_bis = new List<Element>();
           // m_elements_visible.Add(m_position_now);
            m_elements_visible_tmp_bis.Add(m_position_now);
           


            for (int k = 0; k < m_range; k++)
            {

                foreach (Element loc_element in m_elements_visible_tmp_bis)
                    ApplyPattern(loc_element);

                m_elements_visible_tmp_bis.AddRange(m_elements_visible_tmp);
            }

            // Keep only singleton
            foreach (Element loc_elem in m_elements_visible_tmp_bis)
            {
                if (!InListVisible(loc_elem))
                    m_elements_visible.Add(loc_elem);
            }
           // m_elements_visible.Remove(m_position_now);

            
            
        }
  

        public void ApplyPattern(Element par_element)
        {
            switch (m_name_direction)
            {
                case "_north":

                    if (par_element.North != null && !TestElement(par_element.North))
                    {
                        m_elements_visible_tmp.Add(par_element.North);
                            
                    }
                    if (par_element.NorthEast != null && !TestElement(par_element.NorthEast))
                    {
                        m_elements_visible_tmp.Add(par_element.NorthEast);
                          
                    }
                    if (par_element.NorthWest != null && !TestElement(par_element.NorthWest))
                    {
                        m_elements_visible_tmp.Add(par_element.NorthWest);
                           
                    }

                    break;
                        
                case "_north_east":

                    if (par_element.North != null && !TestElement(par_element.North))
                    {
                        m_elements_visible_tmp.Add(par_element.North);
                      
                    }
                    if (par_element.NorthEast != null && !TestElement(par_element.NorthEast))
                    {
                        m_elements_visible_tmp.Add(par_element.NorthEast);
                            
                    }
                    if (par_element.East != null && !TestElement(par_element.East))
                    {
                        m_elements_visible_tmp.Add(par_element.East);
                            
                    }

                    break;

                case "_east":

                    if (par_element.SouthEast != null && !TestElement(par_element.SouthEast))
                    {
                        m_elements_visible_tmp.Add(par_element.SouthEast);
                           
                    }
                    if (par_element.NorthEast != null && !TestElement(par_element.NorthEast))
                    {
                        m_elements_visible_tmp.Add(par_element.NorthEast);
                            
                    }
                    if (par_element.East != null && !TestElement(par_element.East))
                    {
                        m_elements_visible_tmp.Add(par_element.East);
                           
                    }

                    break;

                case "_south_east":

                    if (par_element.SouthEast != null && !TestElement(par_element.SouthEast))
                    {
                        m_elements_visible_tmp.Add(par_element.SouthEast);
                     
                    }
                    if (par_element.South != null && !TestElement(par_element.South))
                    {
                        m_elements_visible_tmp.Add(par_element.South);
                      
                    }
                    if (par_element.East != null && !TestElement(par_element.East))
                    {
                        m_elements_visible_tmp.Add(par_element.East);
                         
                    }

                    break;

                case "_south":

                    if (par_element.SouthEast != null && !TestElement(par_element.SouthEast))
                    {
                        m_elements_visible_tmp.Add(par_element.SouthEast);
                     
                    }
                    if (par_element.South != null && !TestElement(par_element.South))
                    {
                        m_elements_visible_tmp.Add(par_element.South);
                   
                    }
                    if (par_element.SouthWest != null && !TestElement(par_element.SouthWest))
                    {
                        m_elements_visible_tmp.Add(par_element.SouthWest);
                          
                    }

                    break;

                case "_south_west":

                    if (par_element.West != null && !TestElement(par_element.West))
                    {
                        m_elements_visible_tmp.Add(par_element.West);
                   
                    }
                    if (par_element.South != null && !TestElement(par_element.South))
                    {
                        m_elements_visible_tmp.Add(par_element.South);
                        
                    }
                    if (par_element.SouthWest != null && !TestElement(par_element.SouthWest))
                    {
                        m_elements_visible_tmp.Add(par_element.SouthWest);
                          
                    }

                    break;

                case "_west":

                    if (par_element.West != null && !TestElement(par_element.West))
                    {
                        m_elements_visible_tmp.Add(par_element.West);
                        
                    }
                    if (par_element.NorthWest != null && !TestElement(par_element.NorthWest))
                    {
                        m_elements_visible_tmp.Add(par_element.NorthWest);
                           
                    }
                    if (par_element.SouthWest != null && !TestElement(par_element.SouthWest))
                    {
                        m_elements_visible_tmp.Add(par_element.SouthWest);
                        
                    }

                    break;


                case "_north_west":

                    if (par_element.West != null && !TestElement(par_element.West))
                    {
                        m_elements_visible_tmp.Add(par_element.West);
                          
                    }
                    if (par_element.NorthWest != null && !TestElement(par_element.NorthWest))
                    {
                        m_elements_visible_tmp.Add(par_element.NorthWest);
                         
                    }
                    if (par_element.North != null && !TestElement(par_element.North))
                    {
                        m_elements_visible_tmp.Add(par_element.North);
                       
                    }

                    break;
            }
                
            
        }

        public void DeterminedPerception()
        {
            m_perception = new List<Element>();
            m_perception_tmp = new List<Element>();        

            ApplyPerception(0, m_position_now);            

            foreach (Element loc_elem in m_perception_tmp)
            {
                if (!InListPerception(loc_elem))
                    m_perception.Add(loc_elem);
            }
        }

        public void ApplyPerception(int par_range, Element par_element)
        {
            if (par_range < m_range_perception)
            {

                foreach (Element loc_neighboor in par_element.Neighboors)
                    {
                        m_perception_tmp.Add(loc_neighboor);
                        par_range += 1;
                        ApplyPerception(par_range, loc_neighboor);
                        par_range -= 1;                    
                    }
 
            }
        }

        private bool InListPerception(Element par_elem)
        {
            foreach (Element item in m_perception)
            {
                if (item == par_elem)
                    return true;
            }
            return false;
        }

        public bool InListVisible(Element par_elem)
        {
            foreach (Element item in m_elements_visible)
            {
                if (item == par_elem)
                    return true;
            }
            return false;
        }

        public bool InListNeighboors(Element par_elem)
        {
            foreach (Element loc_elem in m_position_now.Neighboors)
            {
                if (loc_elem == par_elem)
                    return true;
            }
            return false;
        }

        private bool TestElement(Element par_elem)
        {
            if (TestRock(par_elem))
            {
                m_element_visited.Add(par_elem);
                return true;
            }

            foreach (Element item in m_element_visited)
            {
                if (item == par_elem)
                {
                    
                    return true;
                }
            }

            m_element_visited.Add(par_elem);
            return false;
        }

        public bool TestVisited(Element par_elem)
        {
            for (int i = 0; i < m_elements_viewed.Count; i++)
            {
                if (par_elem == m_elements_viewed[i])
                    return true;
            }
            return false;
        }

        private bool TestRock(object o)
        {
            if (o is Rock)
                return true;
            return false;    
        }

        public void Aetoile(Element par_target)
        {
            Path loc_path = new Path(null, m_position_now, par_target, this);
            Graph loc_graph = new Graph();
            loc_path = (Path)loc_graph.RechercheSolutionAEtoile(loc_path);

            while (loc_path != null)
            {
                if (loc_path.Position != m_position_now)
                    m_path.Add(loc_path.Position);

                loc_path = (Path)loc_path.Parent;
            }

            if (m_path.Count > 1)
            {
                SetDirection(m_path[m_path.Count - 1]); ;
                m_position_now.Occupants.Remove(this);
                m_position_now = m_path[m_path.Count - 1];
                m_position_now.Occupants.Add(this);
            }
            else if (m_path.Count == 1)
            {
                SetDirection(m_path[0]); ;
                m_position_now.Occupants.Remove(this);
                m_position_now = m_path[0];
                m_position_now.Occupants.Add(this);
            }                             
        }

        public Element PositionNow
        {
            get { return m_position_now; }
            set { m_position_now = value; }
        }

        public Element PositionNext
        {
            get { return m_position_next; }
            set { m_position_next = value; }
        }
        
        public string Direction
        {
            get { return m_name_direction; }
            set { m_name_direction = value; }
        }

        public List<Element> ElementsVisible
        {
            get { return m_elements_visible; }
            set { m_elements_visible = value; }
        }

        public List<Element> ElementsViewed
        {
            get { return m_elements_viewed; }
            set { m_elements_viewed = value; }
        }

        public int Range
        {
            get { return m_range; }
            set { m_range = value; }
        }

        public List<ItemMove> ItemsViewed
        {
            get { return m_item_viewed; }
        }

        public List<Element> ElementsPath
        {
            get { return m_path; }
        }

        public string Name
        {
            get { return m_name; }
            set { m_name = value; }
        }

        public List<Element> Perception
        {
            get { return m_perception; }
            set {  m_perception = value; }
        }

        public List<ItemMove> Children
        {
            get { return m_children; }
        }

        public string TypeDecision
        {
            get { return m_type_decision; }
        }

        public float GrowthMax
        {
            get { return m_growth_max; }
        }

        public float Growth
        {
            get { return m_growth; }
            set { m_growth = value; }
        }

        public bool Killed
        {
            get { return m_is_killed; }
            set { m_is_killed = value; }
        }
        
        public bool WantBreed
        {
            get { return m_want_breed; }
            set { m_want_breed = value; }
        }
    }
}
